/*
 * @Copyright: 2017 www.yyfax.com Inc. All rights reserved.
 */
package com.yyfax.pay.order.service;

import java.util.Date;
import java.util.List;

import javax.annotation.Resource;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;

import com.yyfax.commons.data.AmountUtil;
import com.yyfax.commons.http.HttpUtil;
import com.yyfax.commons.json.JacksonUtil;
import com.yyfax.commons.lang.DateUtil;
import com.yyfax.framework.exception.YYException;
import com.yyfax.framework.model.EmptyContent;
import com.yyfax.framework.service.CacheService;
import com.yyfax.framework.yyfax.CryptUtil;
import com.yyfax.framework.yyfax.DesensitizationUtil;
import com.yyfax.framework.yyfax.enums.CurrencyEnum;
import com.yyfax.framework.yyfax.enums.IdCardTypeEnum;
import com.yyfax.framework.yyfax.enums.TerminalEnum;
import com.yyfax.framework.yyfax.enums.UserTypeEnum;
import com.yyfax.framework.yyfax.enums.YesNoEnum;
import com.yyfax.pay.common.constants.CacheName;
import com.yyfax.pay.common.constants.PayConstants;
import com.yyfax.pay.common.enums.ApiTypeEnum;
import com.yyfax.pay.common.enums.NotifyTaskTypeEnum;
import com.yyfax.pay.common.enums.PayModeEnum;
import com.yyfax.pay.common.enums.PayOrderRedirectTypeEnum;
import com.yyfax.pay.common.enums.PayRspCodeEnum;
import com.yyfax.pay.common.enums.PayStepEnum;
import com.yyfax.pay.common.enums.PayTypeEnum;
import com.yyfax.pay.common.enums.PaymentEnum;
import com.yyfax.pay.common.enums.QueryTaskTypeEnum;
import com.yyfax.pay.common.enums.QueryTypeEnum;
import com.yyfax.pay.common.exception.YYPayException;
import com.yyfax.pay.common.model.to.YYPayRsp;
import com.yyfax.pay.common.util.CommonIdUtils;
import com.yyfax.pay.common.util.QuotaUtil;
import com.yyfax.pay.config.data.service.BankConfigService;
import com.yyfax.pay.config.data.service.MerchantConfigService;
import com.yyfax.pay.config.data.service.MerchantPaymentService;
import com.yyfax.pay.config.data.service.PaymentQuotaService;
import com.yyfax.pay.config.model.po.BankConfigPO;
import com.yyfax.pay.config.model.po.MerchantConfigPO;
import com.yyfax.pay.config.model.po.MerchantPaymentPO;
import com.yyfax.pay.config.model.po.PaymentQuotaPO;
import com.yyfax.pay.order.data.service.PayOrderExtService;
import com.yyfax.pay.order.data.service.PayOrderLogService;
import com.yyfax.pay.order.data.service.PayOrderService;
import com.yyfax.pay.order.data.service.UserAuthService;
import com.yyfax.pay.order.data.service.UserPaymentService;
import com.yyfax.pay.order.model.po.NotifyTaskPO;
import com.yyfax.pay.order.model.po.PayOrderExtPO;
import com.yyfax.pay.order.model.po.PayOrderLogPO;
import com.yyfax.pay.order.model.po.PayOrderPO;
import com.yyfax.pay.order.model.po.UserAuthPO;
import com.yyfax.pay.order.model.po.UserPaymentPO;
import com.yyfax.pay.order.model.to.CounterContent;
import com.yyfax.pay.order.model.to.CounterParams;
import com.yyfax.pay.order.model.to.PayOrderParams;
import com.yyfax.pay.order.model.to.QueryPayOrderContent;
import com.yyfax.pay.order.model.to.ResultNotifyParams;
import com.yyfax.pay.order.model.to.ResultNotifyParams.NotifyParams;
import com.yyfax.pay.order.model.to.WebFormConfirmParams;
import com.yyfax.pay.order.model.to.pay.PayOderReturnDataParams;
import com.yyfax.pay.order.model.to.pay.PayPurchaseApiContent;
import com.yyfax.pay.order.model.to.pay.PayPurchaseApiContent.PayPurchaseApiForm;
import com.yyfax.pay.order.model.to.pay.PaySendVerifyDirectParams;
import com.yyfax.pay.order.model.vo.PayBusiIdVO;
import com.yyfax.pay.order.model.vo.PayOrderVO;
import com.yyfax.pay.order.model.vo.RouteFormVO;
import com.yyfax.pay.order.model.vo.UserAuthVO;
import com.yyfax.pay.order.model.vo.UserDefaultAuthenticationVO;
import com.yyfax.pay.order.model.vo.UserPaymentVO;
import com.yyfax.pay.third.model.to.yyfax.MerchantConfigExtContent;
import com.yyfax.pay.third.model.to.yyfax.NotifyDataBinder;
import com.yyfax.pay.third.model.to.yyfax.OrderNotifyContent;
import com.yyfax.pay.third.model.to.yyfax.PayOrderYYContent;
import com.yyfax.pay.third.model.to.yyfax.PayOrderYYParams;
import com.yyfax.pay.third.model.to.yyfax.QueryCardBinContent;
import com.yyfax.pay.third.model.to.yyfax.QueryPayOrderYYContent;
import com.yyfax.pay.third.model.to.yyfax.QueryPayOrderYYParams;
import com.yyfax.pay.third.model.to.yyfax.SendVerifyCodeYYContent;
import com.yyfax.pay.third.model.to.yyfax.SendVerifyCodeYYParams;
import com.yyfax.pay.third.model.to.yyfax.WebFormYYContent;
import com.yyfax.pay.third.model.to.yyfax.WebFormYYParams;
import com.yyfax.pay.third.service.ThirdPayService;

/**
 * 支付的业务逻辑封装
 * 
 * @author jiangmy
 * @date 2017-04-11 10:13:24
 * @since v1.0.0
 */
@Service
public class PayService {
	private Logger logger = LoggerFactory.getLogger(getClass());

	@Resource(name = "payCacheService")
	private CacheService cacheService;

	@Autowired
	private AdapterService adapterService;

	@Autowired
	private RouteService routeService;

	@Autowired
	private PayOrderService payOrderService;

	@Autowired
	private UserPaymentService userPaymentService;

	@Autowired
	private PayOrderExtService payOrderExtService;

	@Autowired
	private UserAuthService userAuthService;

	@Autowired
	private AuthService authService;

	@Autowired
	private MerchantConfigService merchantConfigService;

	@Autowired
	private PayOrderLogService payOrderLogService;

	@Autowired
	private PayCommonService payCommonService;

	@Autowired
	private PayConstants payConstants;

	@Autowired
	private PaymentQuotaService paymentQuotaService;

	@Autowired
	private BankConfigService bankConfigService;

	@Autowired
	private MerchantPaymentService merchantPaymentService;

	/**
	 * 订单信息持久化
	 * 
	 * @author buyi
	 * @date 2017-04-14 17:27:16
	 * @since v1.0.0
	 * @param params
	 * @return 返回支付订单业务主键
	 * @throws Exception
	 */
	public PayOrderVO savePayOrder(PayOrderParams params) throws Exception {
		PayOrderVO payOrderVO = new PayOrderVO();

		PayOrderPO po = new PayOrderPO();
		BeanUtils.copyProperties(params, po);
		po.setCurrency(CurrencyEnum.RMB.getValue());

		try {
			payOrderService.saveAndReturnBusiKey(po);

			BeanUtils.copyProperties(po, payOrderVO);
		} catch (DuplicateKeyException e) {
			logger.info("保存订单信息出现主键冲突，走幂等流程,查询订单信息");
			// 查询订单状态
			payOrderVO = payOrderService.queryPayOrderVOByOrderId(params.getOrderId(), params.getMerchantCode());
		}

		return payOrderVO;
	}

	/**
	 * 持久化订单信息，以及订单附加信息<br>
	 * 该接口支持幂等
	 * 
	 * @author buyi
	 * @date 2017-04-27 15:25:04
	 * @since v1.0.0
	 * @param params
	 * @param actPaymentEnum
	 * @param payMode
	 * @param apiType
	 * @param idNo
	 * @param name
	 * @param cardNo
	 * @param bankCode
	 * @return
	 * @throws Throwable
	 */
	public PayOrderVO savePayOrder(PaySendVerifyDirectParams params, PaymentEnum actPaymentEnum, PayModeEnum payMode, ApiTypeEnum apiType,
			String idNo, String name, String cardNo, String bankCode, String bankName) throws Throwable {
		PayOrderVO payOrderVO = null;

		// 业务主键
		String payOrderId = null;
		try {
			PayOrderPO po = new PayOrderPO();
			po.setMerchantCode(params.getMerchantCode());
			po.setUserId(params.getUserId());
			po.setOrderId(params.getOrderId());
			po.setTerminal(params.getTerminal());
			po.setOrderTime(params.getOrderTime());
			po.setExpireTime(params.getExpireTime());
			po.setPaymentCode(params.getPaymentCode());
			po.setCurrency(CurrencyEnum.RMB.getValue());
			po.setAmount(params.getAmount());
			po.setReturnUrl("");
			po.setNotifyUrl(params.getNotifyUrl());
			po.setUserIp(params.getUserIp());
			po.setServerIp(params.getServerIp());
			payOrderId = payOrderService.saveAndReturnBusiKey(po);

			payOrderVO = new PayOrderVO();
			BeanUtils.copyProperties(po, payOrderVO);
		} catch (DuplicateKeyException e) {
			logger.info("保存订单信息出现主键冲突，走幂等流程");
			payOrderVO = payOrderService.queryPayOrderVOByOrderId(params.getOrderId(), params.getMerchantCode());
		}

		if (payOrderVO == null) {
			throw new YYException(PayRspCodeEnum.COMMON_ERROR_UPDATE_FAILED, "保存订单信息异常");
		}

		// 保存订单附加信息
		try {
			// PayOrderExtPO payOrderExtPO = savePayOrderExt(payOrderVO,
			// actPaymentEnum, idNo, name, cardNo, bankCode, bankName, cardNo,
			// apiType,
			// payConstants.getReturnUrl(actPaymentEnum.getValue()),
			// payConstants.getNotifyUrl(actPaymentEnum.getValue()));
			PayOrderExtPO payOrderExtPO = savePayOrderExt(payOrderVO, actPaymentEnum, idNo, name, cardNo, bankCode, bankName, payMode.getValue(),
					apiType, payConstants.getReturnUrl(actPaymentEnum.getValue()), payConstants.getNotifyUrl(actPaymentEnum.getValue()));

			payOrderVO.setPayOrderExtPO(payOrderExtPO);
		} catch (DuplicateKeyException e) {
			logger.warn("保存支付附加信息出现主键冲突，不处理,exception:{}", e.getMessage());
			payOrderVO = payOrderService.queryPayOrderVO(payOrderId, params.getMerchantCode());
		}

		return payOrderVO;
	}

	/**
	 * 支付订单附加信息持久化(幂等)
	 * 
	 * @author buyi
	 * @date 2017-04-24 15:59:58
	 * @since v1.0.0
	 * @param payOrderPO
	 * @param payment
	 *        支付通道
	 * @param idNo
	 *        证件号
	 * @param name
	 *        姓名
	 * @param cardNo
	 *        银行卡号
	 * @param bankCode
	 *        银行编号
	 * @param payMode
	 *        支付模式
	 * @param apiType
	 *        接口类型
	 * @param returnUrl
	 *        回显地址
	 * @param notifyUrl
	 *        通知地址
	 * @return
	 * @throws Throwable
	 */
	private PayOrderExtPO savePayOrderExt(PayOrderPO payOrderPO, PaymentEnum payment, String idNo, String name, String cardNo, String bankCode,
			String bankName, String payMode, ApiTypeEnum apiType, String returnUrl, String notifyUrl) throws Throwable {
		PayOrderExtPO payOrderExtPO = new PayOrderExtPO();

		BeanUtils.copyProperties(payOrderPO, payOrderExtPO);
		payOrderExtPO.setActPaymentCode(payment.getValue());// 实际支付通道
		payOrderExtPO.setIdType(IdCardTypeEnum.PRC_ID.getValue());// 证件类型，默认为身份证

		payOrderExtPO.setIdNo(DesensitizationUtil.convertIdCard(idNo)); // 脱敏身份证号
		payOrderExtPO.setIdNoCip(CryptUtil.encrypt(idNo)); // 加密身份证号
		payOrderExtPO.setName(name); // 持卡人姓名
		payOrderExtPO.setCardNo(DesensitizationUtil.convertBankCard(cardNo)); // 脱敏银行卡
		payOrderExtPO.setCardNoCip(CryptUtil.encrypt(cardNo)); // 加密银行卡

		payOrderExtPO.setBankCode(bankCode);
		payOrderExtPO.setBankName(bankName);
		payOrderExtPO.setPayMode(payMode); // 支付模式，默认鉴权支付
		payOrderExtPO.setApiType(apiType.getValue());

		payOrderExtPO.setPayReturnUrl(returnUrl); // 回显地址
		payOrderExtPO.setPayNotifyUrl(notifyUrl); // 异步回调地址

		try {
			payOrderExtService.save(payOrderExtPO);
		} catch (DuplicateKeyException e) {
			logger.info("保存订单主键信息出现主键冲突，走幂等流程");
			payOrderExtPO = payOrderExtService.queryByPayOrderId(payOrderPO.getPayOrderId(), payOrderPO.getMerchantCode());

		}
		return payOrderExtPO;
	}

	/**
	 * 支付下单 ——网关模式<br>
	 * 支持订单幂等<br>
	 * 路由规则跟变天似的，杀了我一个措手不及
	 * 
	 * @author buyi
	 * @date 2017-05-05 09:39:59
	 * @since v1.0.0
	 * @param payOrderVO
	 * @param reqIdNo
	 * @param reqName
	 * @param reqCardNo
	 * @return
	 * @throws Throwable
	 */
	public RouteFormVO<?> payRoutePage(PayOrderVO payOrderVO, String reqIdNo, String reqName, String reqCardNo) throws Throwable {

		RouteFormVO<?> idempotentPayOrder = processIdempotentPayOrder(payOrderVO, reqIdNo, reqName, reqCardNo);
		if (idempotentPayOrder != null) {
			return idempotentPayOrder;
		}

		// 获取用户有效支付鉴权信息
		UserDefaultAuthenticationVO userDefaultAuthenticationVO = getUserDefaulAuthenticationInfo(payOrderVO.getMerchantCode(),
				payOrderVO.getUserId());

		if (userDefaultAuthenticationVO == null || userDefaultAuthenticationVO.getUserPaymentVO() == null) {// 用户不存在有效的支付鉴权信息,则跳转到实名认证页面
			logger.debug("订单：{}存，不存在有效的鉴权信息，跳转到实名认证页面", payOrderVO.getPayOrderId());
			UserAuthPO userAuthPO = userDefaultAuthenticationVO != null ? userDefaultAuthenticationVO.getUserAuthPO() : null;

			if (StringUtils.isNotBlank(reqCardNo)) {// 如果传参带银行卡，则直接跳转到收银台
				savePayOrderExtForForm(payOrderVO, reqIdNo, reqName, reqCardNo);

				CounterParams params = new CounterParams();
				params.setPayOrderId(payOrderVO.getPayOrderId());
				// params.setUserIp(payOrderVO.getUserIp());
				params.setMerchantCode(payOrderVO.getMerchantCode());
				params.setPaymentCode(payOrderVO.getPayOrderExtPO().getActPaymentCode());
				params.setSign(params.sign(payConstants.getSignKey()));

				return new RouteFormVO<CounterParams>(PayOrderRedirectTypeEnum.COUNTER.getValue(), params);
			}

			WebFormConfirmParams params = new WebFormConfirmParams();
			params.setPayOrderId(payOrderVO.getPayOrderId());
			params.setMerchantCode(payOrderVO.getMerchantCode());
			// params.setUserId(payOrderVO.getUserId());
			params.setIdNo(userAuthPO == null ? reqIdNo : CryptUtil.decrypt(userAuthPO.getIdNoCip()));
			params.setName(userAuthPO == null ? reqName : userAuthPO.getName());
			params.setCardNo(reqCardNo);
			params.setSign(params.sign(payConstants.getSignKey())); // 设置签名//生成签名串

			return new RouteFormVO<WebFormConfirmParams>(PayOrderRedirectTypeEnum.REAL_NAME.getValue(), params);
		}

		/****** 用户存在有效的鉴权信息 **/

		if (TerminalEnum.H5.isValue(payOrderVO.getTerminal())) { // 如果是移动端,则跳转到收银台
			logger.debug("订单：{}存，存在有效的鉴权信息，且为H5移动端接入，跳转到收银台", payOrderVO.getPayOrderId());

			// 保存订单附加信息
			UserPaymentVO userPaymentVO = userDefaultAuthenticationVO.getUserPaymentVO();
			savePayOrderExtForForm(payOrderVO, userPaymentVO.getPaymentCode(), userPaymentVO.getBankCode(), userPaymentVO.getBankName(),
					CryptUtil.decrypt(userPaymentVO.getIdNoCip()), userPaymentVO.getName(), CryptUtil.decrypt(userPaymentVO.getCardNoCip()));

			CounterParams params = new CounterParams();
			params.setPayOrderId(payOrderVO.getPayOrderId());
			// params.setUserIp(payOrderVO.getUserIp());
			params.setMerchantCode(payOrderVO.getMerchantCode());
			params.setPaymentCode(payOrderVO.getPayOrderExtPO().getActPaymentCode());
			params.setSign(params.sign(payConstants.getSignKey()));

			return new RouteFormVO<CounterParams>(PayOrderRedirectTypeEnum.COUNTER.getValue(), params);
		}

		if (!TerminalEnum.PC.isValue(payOrderVO.getTerminal())) {
			throw new YYException(PayRspCodeEnum.COMMON_ERROR_PARAMETER_INVALID, "不支持该终端类型");
		}

		logger.debug("订单：{}存，存在有效的鉴权信息，且为PC端接入，跳转到支付模式选择页面", payOrderVO.getPayOrderId());

		// PC端，则跳转到支付选择页面
		WebFormConfirmParams params = new WebFormConfirmParams();
		params.setPayOrderId(payOrderVO.getPayOrderId());
		params.setMerchantCode(payOrderVO.getMerchantCode());
		// params.setUserId(payOrderVO.getUserId());
		params.setIdNo(reqIdNo);
		params.setName(reqName);
		params.setCardNo(reqCardNo);
		params.setSign(params.sign(payConstants.getSignKey())); // 设置签名//生成签名串

		return new RouteFormVO<WebFormConfirmParams>(PayOrderRedirectTypeEnum.SELECT.getValue(), params);
	}

	private RouteFormVO<?> processIdempotentPayOrder(PayOrderVO payOrderVO, String reqIdNo, String reqName, String reqCardNo) throws YYPayException {
		// 支付订单幂等路由

		PayOrderLogPO resultStep = payOrderVO.getStepLast(PayStepEnum.STEP4_PAY_RESULT);
		// 如果订单已经有结果回调，则返回结果
		if (resultStep != null) {
			throw new YYPayException(PayRspCodeEnum.PAY_CODE_PAY_ORDER_PAID, "订单已经支付，或者已经产生异步回调，订单号：" + payOrderVO.getPayOrderId());
		}

		PayOrderLogPO submitOrderStep = payOrderVO.getStepLast(PayStepEnum.STEP2_SUBMIT_ORDER);
		// 如果订单请求过支付，且状态为成功、超时、挂起,则返回结果
		if (submitOrderStep != null && (PayRspCodeEnum.SUCCESS.isValue(submitOrderStep.getResultCode())
				|| PayRspCodeEnum.PAY_CODE_REQ_TIME_OUT.isValue(submitOrderStep.getResultCode())
				|| PayRspCodeEnum.PAY_CODE_DEALING.isValue(submitOrderStep.getResultCode()))) {

			throw new YYPayException(PayRspCodeEnum.PAY_CODE_PAY_ORDER_PAID, "订单已经支付，或者已经产生异步回调，订单号：" + payOrderVO.getPayOrderId());
		}

		if (payOrderVO.getStepSuccess(PayStepEnum.STEP1_SUBMIT_FORM) != null && payOrderVO.getPayOrderExtPO() != null) { // 如果已经提交表单，则直接跳转到收银台
			logger.debug("订单：{}存，走幂等流程，跳转到收银台", payOrderVO.getPayOrderId());
			CounterParams params = new CounterParams();
			params.setPayOrderId(payOrderVO.getPayOrderId());
			// params.setUserIp(payOrderVO.getUserIp());
			params.setMerchantCode(payOrderVO.getMerchantCode());
			params.setPaymentCode(payOrderVO.getPayOrderExtPO().getActPaymentCode());
			params.setSign(params.sign(payConstants.getSignKey()));

			return new RouteFormVO<CounterParams>(PayOrderRedirectTypeEnum.COUNTER.getValue(), params);
		}

		return null;
	}

	/**
	 * 封装表单提交参数
	 * 
	 * @author buyi
	 * @date 2017-04-13 18:27:52
	 * @since v1.0.0
	 * @param params
	 * @return
	 * @throws Throwable
	 */
	public WebFormYYContent<?> getWebForm(WebFormConfirmParams params) throws Throwable {
		// 1.查询银行卡所属银行
		QueryCardBinContent cardBinContent = payCommonService.queryCardBin(params.getMerchantCode(), params.getCardNo());

		// 2.商户订单号
		PayOrderPO payOrderPO = payOrderService.queryByPayOrderId(params.getMerchantCode(), params.getPayOrderId());
		if (payOrderPO == null) {
			throw new YYException(PayRspCodeEnum.COMMON_ERROR_OBJECT_NOT_FOUND.getValue(),
					"订单缺失，payOrderId:[" + params.getPayOrderId() + "],merchantCode:[]", "订单不存在");
		}

		// 校验身份证号
		userAuthService.checkIdNoAndName(params.getMerchantCode(), payOrderPO.getUserId(), params.getIdNo(), params.getName());

		// 3.根据商户编号、和银行编号查询支付通道
		PaymentEnum paymentEnum = routeService.routePay(params.getMerchantCode(), cardBinContent.getBankCode(), payOrderPO.getPaymentCode(),
				params.getPayMode(), null);
		if (paymentEnum == null) {// 如果支付通道没有命中
			throw new YYException(PayRspCodeEnum.PAY_CODE_MERCHANT_NOT_SUPPORT_PAYMENT, "支付通道非法，返回null");
		}

		// 4.转换成相应支付通道form
		ThirdPayService service = adapterService.adapterPayService(paymentEnum);
		WebFormYYParams webFormYYParams = new WebFormYYParams();
		// 封装 webForm请求参数
		BeanUtils.copyProperties(payOrderPO, webFormYYParams);
		webFormYYParams.setPaymentCode(paymentEnum.getValue());
		webFormYYParams.setIdNo(params.getIdNo()); // 身份证号
		webFormYYParams.setName(params.getName()); // 持卡人姓名
		webFormYYParams.setCardNo(params.getCardNo()); // 银行卡
		webFormYYParams.setBankName(cardBinContent.getBankName());
		webFormYYParams.setPayMode(params.getPayMode()); // 支付模式

		@SuppressWarnings("rawtypes")
		WebFormYYContent webForm = service.transformWebForm(webFormYYParams);

		// 5.保存订单附加信息
		PayOrderExtPO orderExtPO = savePayOrderExt(payOrderPO, paymentEnum, params.getIdNo(), params.getName(), params.getCardNo(),
				cardBinContent.getBankCode(), cardBinContent.getBankName(), params.getPayMode(),
				paymentEnum == PaymentEnum.LIAN_LIAN ? ApiTypeEnum.WEB : ApiTypeEnum.API, webForm.getReturnUrl(), webForm.getNotifyUrl());

		// 插入日志
		savePayOrderLog(new PayOrderVO(payOrderPO, orderExtPO), PayStepEnum.STEP1_SUBMIT_FORM, webForm.getUrl(),
				JacksonUtil.noemptyMapper.toJson(webForm.getForm()), null, null, PayRspCodeEnum.SUCCESS.getValue(), "成功，提交表单，跳转到收银台", YesNoEnum.NO,
				null);

		return webForm;
	}

	public UserDefaultAuthenticationVO getUserDefaulAuthenticationInfo(String merchantCode, String userId) throws Exception {
		// 获取用户鉴权信息
		UserAuthVO userAuthVO = userAuthService.queryUserAuthVO(merchantCode, userId);
		if (userAuthVO == null || userAuthVO.getUserPaymentVOs().isEmpty()) {
			return null;
		}

		UserDefaultAuthenticationVO userDefaultAuthenticationVO = new UserDefaultAuthenticationVO(userAuthVO);

		List<MerchantPaymentPO> merchantPaymentPOs = merchantPaymentService.queryList(userAuthVO.getMerchantCode(), PayTypeEnum.PAY.getValue());
		UserPaymentVO userPayment = userAuthVO.getUserPayment(merchantPaymentPOs);
		if (userPayment != null) {
			userDefaultAuthenticationVO.setUserPaymentVO(userPayment);
		}

		return userDefaultAuthenticationVO;
	}

	public void savePayOrderExtForForm(PayOrderVO payOrderVO, String idNo, String name, String cardNo) throws Throwable {
		QueryCardBinContent cardBin = payCommonService.queryCardBin(payOrderVO.getMerchantCode(), cardNo);
		// 获取相应支付渠道
		PaymentEnum paymentEnum = routeService.routePay(payOrderVO.getMerchantCode(), cardBin.getBankCode(), payOrderVO.getPaymentCode(),
				PayModeEnum.AUTH_PAY.getValue(), ApiTypeEnum.API.getValue());

		savePayOrderExtForForm(payOrderVO, paymentEnum.getValue(), cardBin.getBankCode(), cardBin.getBankName(), idNo, name, cardNo);
	}

	/**
	 * 生成订单附加信息，已经表单提交日志
	 * 
	 * @author buyi
	 * @date 2017-05-05 08:51:11
	 * @since v1.0.0
	 * @param payOrderVO
	 * @param actPaymentCode
	 * @param bankCode
	 * @param bankName
	 * @param idNo
	 * @param name
	 * @param cardNo
	 * @throws Throwable
	 */
	public void savePayOrderExtForForm(PayOrderVO payOrderVO, String actPaymentCode, String bankCode, String bankName, String idNo, String name,
			String cardNo) throws Throwable {

		// 根据商户编号、和银行编号查询支付通道
		PaymentEnum paymentEnum = routeService.routePay(payOrderVO.getMerchantCode(), bankCode, actPaymentCode, PayModeEnum.AUTH_PAY.getValue(),
				null);
		if (paymentEnum == null) {// 如果支付通道没有命中
			throw new YYException(PayRspCodeEnum.PAY_CODE_MERCHANT_NOT_SUPPORT_PAYMENT, "支付通道非法，返回null");
		}

		// 保存订单附加信息
		PayOrderExtPO payOrderExt = savePayOrderExt(payOrderVO, paymentEnum, idNo, name, cardNo, bankCode, bankName, PayModeEnum.AUTH_PAY.getValue(),
				paymentEnum == PaymentEnum.LIAN_LIAN ? ApiTypeEnum.WEB : ApiTypeEnum.API, payConstants.getReturnUrl(paymentEnum.getValue()),
				payConstants.getNotifyUrl(paymentEnum.getValue()));

		payOrderVO.setPayOrderExtPO(payOrderExt);

		// 获取商户支付通道扩展信息
		ThirdPayService service = adapterService.adapterPayService(paymentEnum);
		MerchantConfigExtContent merchantConfigExt = service.queryMerchantConfigExt(payOrderVO.getMerchantCode(), PayTypeEnum.PAY,
				PayModeEnum.AUTH_PAY);

		// 插入日志
		savePayOrderLog(payOrderVO, PayStepEnum.STEP1_SUBMIT_FORM, merchantConfigExt.getFormUrl(), null, null, null,
				PayRspCodeEnum.SUCCESS.getValue(), "成功，移动端提交表单，跳转到收银台", YesNoEnum.NO, null);
	}

	/**
	 * 封装支付同步回显参数
	 * 
	 * @author buyi
	 * @date 2017-04-15 11:19:22
	 * @since v1.0.0
	 * @param resultCode
	 * @param resultMsg
	 * @param merchantCode
	 * @param payOrderId
	 * @return
	 * @throws Exception
	 */
	public PayPurchaseApiContent transformPayReturnUrl(Integer resultCode, String resultMsg, String merchantCode, String payOrderId)
			throws Exception {
		PayOrderPO payOrderPO = payOrderService.queryByPayOrderId(merchantCode, payOrderId);
		MerchantConfigPO merchantConfigPO = merchantConfigService.queryByMerchantCode(merchantCode);

		PayOderReturnDataParams returnData = new PayOderReturnDataParams();
		returnData.setResultCode(resultCode);
		returnData.setResultMsg(resultMsg);
		returnData.setMerchantCode(payOrderPO.getMerchantCode());
		returnData.setOrderId(payOrderPO.getOrderId());
		returnData.setAmount(payOrderPO.getAmount());

		PayPurchaseApiForm form = new PayPurchaseApiForm();
		form.setSign(returnData.sign(merchantConfigPO.getSignKey()));
		form.setParams(JacksonUtil.noemptyMapper.toJson(returnData));

		PayPurchaseApiContent content = new PayPurchaseApiContent();
		content.setUrl(payOrderPO.getReturnUrl());
		content.setForm(form);

		return content;
	}

	// public void saveUserAuthAndPayment(PayOrderVO payOrderVO) throws
	// Exception {
	//
	// // 查询数据实名信息
	// UserAuthPO authPO =
	// userAuthService.queryByUserIdAndMerchantCode(payOrderVO.getUserId(),
	// payOrderVO.getMerchantCode());
	// String userAuthId = null;
	// if (authPO != null) {// 如果实名信息存在
	// if (!StringUtils.equals(payOrderVO.getPayOrderExtPO().getIdNoCip(),
	// authPO.getIdNoCip())) {// 身份证号不不一致，则报错
	// logger.error("商户：{}，用户：{}实名认证信息已存在");
	// return;
	// }
	//
	// userAuthId = authPO.getUserAuthId();
	//
	// } else { // 如果实名信息不存在，则保存实名信息
	// UserAuthPO userAuthPO = new UserAuthPO();
	// BeanUtils.copyProperties(payOrderVO.getPayOrderExtPO(), userAuthPO);
	// userAuthId = userAuthService.saveAndReturnUserAuthId(userAuthPO);
	// }
	//
	// if (StringUtils.isBlank(userAuthId)) {
	// return;
	// }
	//
	// // 判断用户支付认证信息
	// UserPaymentPO paymentPO =
	// userPaymentService.query(payOrderVO.getMerchantCode(),
	// payOrderVO.getUserId(),
	// payOrderVO.getPayOrderExtPO().getActPaymentCode());
	// if (paymentPO != null && YesNoEnum.YES.isValue(paymentPO.getState())) {//
	// 支付通道信息
	// logger.debug("商户：{}，用户：{}，支付通道：{}，存在有效的支付认证信息，不做操作",
	// payOrderVO.getMerchantCode(), payOrderVO.getUserId(),
	// payOrderVO.getPayOrderExtPO().getActPaymentCode());
	// return;
	// }
	//
	// UserPaymentPO userPaymentPO = new UserPaymentPO();
	// BeanUtils.copyProperties(payOrderVO.getPayOrderExtPO(), userPaymentPO);
	// userPaymentPO.setUserAuthId(userAuthId);
	// userPaymentPO.setPaymentCode(payOrderVO.getPayOrderExtPO().getActPaymentCode());
	//
	// if (paymentPO == null) {
	// userPaymentService.save(userPaymentPO);
	// } else {
	// userPaymentPO.setState(YesNoEnum.YES.getValue());
	// userPaymentService.modify(userPaymentPO);
	// }
	// }

	/**
	 * 保存支付日志<br>
	 * 如果保存日志异常，则直接捕捉掉，不外抛
	 * 
	 * @author buyi
	 * @date 2017-04-17 17:31:44
	 * @since v1.0.0
	 * @param payOrderVO
	 * @param step
	 * @param reqUrl
	 * @param reqContent
	 * @param rspContent
	 * @param serialNo
	 * @param resultCode
	 * @param resultMsg
	 * @param isClose
	 * @params remark
	 * @return
	 * @throws Exception
	 */
	public PayOrderLogPO savePayOrderLog(PayOrderVO payOrderVO, PayStepEnum step, String reqUrl, String reqContent, String rspContent,
			String serialNo, Integer resultCode, String resultMsg, YesNoEnum isClose, String remark) throws Exception {
		PayOrderLogPO payOrderLogPO = new PayOrderLogPO();
		BeanUtils.copyProperties(payOrderVO, payOrderLogPO);
		BeanUtils.copyProperties(payOrderVO.getPayOrderExtPO(), payOrderLogPO);
		payOrderLogPO.setPaymentCode(payOrderVO.getPayOrderExtPO().getActPaymentCode());

		payOrderLogPO.setStep(step.getValue());
		payOrderLogPO.setReqUrl(reqUrl);
		payOrderLogPO.setReqContent(reqContent);
		payOrderLogPO.setRspContent(rspContent);
		payOrderLogPO.setSerialNo(serialNo);
		payOrderLogPO.setResultCode(resultCode);
		payOrderLogPO.setResultMsg(resultMsg);
		payOrderLogPO.setClosed(isClose.getValue());
		payOrderLogPO.setRemark(remark);
		payOrderLogService.save(payOrderLogPO);

		return payOrderLogPO;
	}

	/**
	 * 向第三方通道查询订单,并将查询结果更新到本地
	 * 
	 * @author jiangmy
	 * @date 2017-04-18 09:02:01
	 * @since v1.0.0
	 * @param payOrder
	 * @throws Exception
	 */
	public PayOrderVO queryPayOrder(String orderId, String merchantCode, QueryTypeEnum queryType) throws Exception {
		// 1.查询订单封装VO
		PayOrderVO payOrderVO = payOrderService.queryPayOrderVOByOrderId(orderId, merchantCode);
		if (payOrderVO == null) {
			return null;
		}

		// 2.判断VO是否已关闭,或者已经异步回调， 关闭则直接返回
		if (payOrderVO.isClose() || payOrderVO.getStepLast(PayStepEnum.STEP4_PAY_RESULT) != null) {
			logger.info("查询订单{}，订单已经关闭，或者已经产生异步回调，不处理", payOrderVO.getPayOrderId());
			return payOrderVO;
		}

		// 3.订单是否查询过, 如果状态为成功，或者支付失败，则返回
		PayOrderLogPO stepQueryResult = payOrderVO.getStepLast(PayStepEnum.STEP3_QUERY_RESULT);
		if (stepQueryResult != null && (PayRspCodeEnum.SUCCESS.isValue(stepQueryResult.getResultCode())
				|| PayRspCodeEnum.PAY_CODE_PAY_FAIL.isValue(stepQueryResult.getResultCode()))) {

			// 如果是查询类型是商户主动查询,或者（查询类型是系统自动查询，且查询结果步骤是自动查询），则直接返回结果
			// 判断查询类型，后期新增一个预留字段，因为紧急版本1.2.1没有时间改动，先根据remark判断，edit by buyi
			if (QueryTypeEnum.INITIATIVE.equals(queryType)
					|| (QueryTypeEnum.AUTO.equals(queryType) && StringUtils.equals(stepQueryResult.getRemark(), QueryTypeEnum.AUTO.getRemark()))) {
				logger.info("查询订单{}，订单已经产生结果:{}-{}", payOrderVO.getPayOrderId(), stepQueryResult.getResultCode(), stepQueryResult.getResultMsg());
				return payOrderVO;
			}

			logger.info("查询订单{}，已存在查询结果步骤，查询类型：{}，查询结果步骤类型：{}，继续执行支付结果查询", payOrderVO.getPayOrderId(), queryType.getRemark(),
					stepQueryResult.getRemark());
		}

		// 4.没有回调记录,但是已经提交支付,执行查询
		PayOrderLogPO stepSubmitOrder = payOrderVO.getStepLast(PayStepEnum.STEP2_SUBMIT_ORDER);
		// 查询，放款条件限制，只要有支付请求，就可以查询，edit by buyi since 1.2.1
		// if (stepSubmitOrder != null && (PayRspCodeEnum.SUCCESS.isValue(stepSubmitOrder.getResultCode())
		// || PayRspCodeEnum.PAY_CODE_DEALING.isValue(stepSubmitOrder.getResultCode())
		// || PayRspCodeEnum.PAY_CODE_REQ_TIME_OUT.isValue(stepSubmitOrder.getResultCode()))) {
		if (stepSubmitOrder != null) {

			PaymentEnum payment = PaymentEnum.fromValue(payOrderVO.getPayOrderExtPO().getActPaymentCode());

			ThirdPayService thirdPayService = adapterService.adapterPayService(payment);
			if (thirdPayService == null) {
				throw new YYPayException(PayRspCodeEnum.PAY_CODE_ILLEGAL_PAYMENT_CODE, "支付通道:" + payment);
			}

			QueryPayOrderYYParams params = new QueryPayOrderYYParams();
			params.setMerchantCode(merchantCode);
			params.setPayOrderId(payOrderVO.getPayOrderId());
			params.setOrderId(payOrderVO.getOrderId());
			params.setOrderTime(payOrderVO.getOrderTime());
			params.setUserId(payOrderVO.getUserId());

			QueryPayOrderYYContent queryOrder = thirdPayService.queryOrder(params);
			// 查询失败
			if (queryOrder == null) {
				throw new YYPayException(PayRspCodeEnum.COMMON_ERR0R_NET_ERROR, "查询第三方订单失败:" + orderId);
			}

			// 处理中 或 请求超时
			if (PayRspCodeEnum.PAY_CODE_DEALING.isValue(queryOrder.getResultCode())
					|| PayRspCodeEnum.PAY_CODE_REQ_TIME_OUT.isValue(queryOrder.getResultCode())) {
				throw new YYPayException(PayRspCodeEnum.COMMON_ERR0R_NET_ERROR, "查询第三方订单结果:" + PayRspCodeEnum.fromValue(queryOrder.getResultCode()));
			}

			// 保存日志
			PayOrderLogPO logPO = savePayOrderLog(payOrderVO, PayStepEnum.STEP3_QUERY_RESULT, queryOrder.getReqUrl(), queryOrder.getReqContent(),
					queryOrder.getRspContent(), queryOrder.getSerialNo(), queryOrder.getResultCode(), queryOrder.getResultMsg(), YesNoEnum.NO,
					queryType.getRemark());

			payOrderVO.addOrderLogPO(logPO);

			// if (QueryTypeEnum.AUTO.equals(queryType)) { // 如果自动查询，则生成异步通知任务
			// // 插入异步通知任务
			// saveNotifyTask(payOrderVO, queryOrder.getResultCode(),
			// queryOrder.getResultMsg(), queryOrder.getSerialNo());
			// }
		}

		return payOrderVO;
	}

	/**
	 * 查询并向商户方回调订单,并将结果更新到本地
	 * 
	 * @deprecated 作废咯，收敛post请求，since 2.1.5 edit by buyi。新方法见 {@linkplain PayService#parseNotify(NotifyDataBinder)}
	 * @author jiangmy
	 * @date 2017-04-18 16:51:33
	 * @since v1.0.0
	 * @param orderId
	 * @param merchantCode
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public PayOrderVO notifyPayOrder(NotifyTaskPO task) throws Exception {
		// 1.查询订单封装VO
		PayOrderVO payOrderVO = payOrderService.queryPayOrderVOByOrderId(task.getOrderId(), task.getMerchantCode());
		if (payOrderVO == null) {
			return null;
		}
		// 2.判断VO是否已关闭, 关闭则直接返回
		if (payOrderVO.isClose()) {
			return payOrderVO;
		}
		// 3.订单未关闭,但已经回调成功
		if (payOrderVO.getStepSuccess(PayStepEnum.STEP5_NOTIFY) != null) {
			PayOrderLogPO logPO = savePayOrderLog(payOrderVO, PayStepEnum.STEP5_NOTIFY, task.getNotifyUrl(), task.getReqContent(), null, null, 0,
					"系统自动关闭订单", YesNoEnum.YES, null);
			payOrderVO.getOrderLogPOs().add(logPO);
			return payOrderVO;
		}

		// 4.支付通道已经回调,还未回调商户方
		// if (payOrderVO.getStepSuccess(PayStepEnum.STEP4_PAY_RESULT) != null)
		// {
		PayOrderLogPO resultStep = payOrderVO.getStepLast(PayStepEnum.STEP4_PAY_RESULT);
		if (resultStep != null) {
			String rspJson = HttpUtil.post(task.getNotifyUrl(), task.getReqContent());
			if (StringUtils.isBlank(rspJson)) {
				logger.error("异步通知商户支付结果失败:{}", task.getOrderId());
				savePayOrderLog(payOrderVO, PayStepEnum.STEP5_NOTIFY, task.getNotifyUrl(), task.getReqContent(), rspJson, null,
						PayRspCodeEnum.COMMON_ERR0R_NET_ERROR.getValue(), "请求连接，返回空", YesNoEnum.NO, null);
				return null;
			}

			YYPayRsp<EmptyContent> rsp = JacksonUtil.fromJson(rspJson, YYPayRsp.class, EmptyContent.class);
			if (rsp == null) {
				logger.error("异步通知商户支付结果失败:{} {}", task.getOrderId(), rspJson);
				savePayOrderLog(payOrderVO, PayStepEnum.STEP5_NOTIFY, task.getNotifyUrl(), task.getReqContent(), rspJson, null,
						PayRspCodeEnum.PAY_CODE_ILLEGAL_FORMAT_RSP.getValue(), "响应参数不正确", YesNoEnum.NO, null);

				return null;
			}

			YesNoEnum isClose = YesNoEnum.NO;
			if (PayRspCodeEnum.SUCCESS.isValue(rsp.getCode())) { // 如果相应成功则关闭订单
				isClose = YesNoEnum.YES;
			}

			logger.info("异步通知商户支付结果:{} {}", task.getOrderId(), rspJson);
			PayOrderLogPO logPO = savePayOrderLog(payOrderVO, PayStepEnum.STEP5_NOTIFY, task.getNotifyUrl(), task.getReqContent(), rspJson, null,
					rsp.getCode(), rsp.getMessage(), isClose, null);

			payOrderVO.addOrderLogPO(logPO);
		} else {
			logger.warn("orderId:{},merchantCode:{}发起异步通知异常，没有接收到支付通道的异步通知结果", payOrderVO.getOrderId(), payOrderVO.getMerchantCode());
		}

		return payOrderVO;
	}

	/**
	 * 解析异步通知结果<br>
	 * 真心话：支付的交易结果通过log来判断，特么恶心到我了
	 * 
	 * @author buyi
	 * @date 2018-05-25 20:32:52
	 * @since v2.1.5
	 * @param binder
	 * @throws Exception
	 */
	public void parseNotify(NotifyDataBinder binder) throws Exception {
		NotifyTaskPO task = binder.getTask();

		// 1.查询订单封装VO
		PayOrderVO payOrderVO = payOrderService.queryPayOrderVOByOrderId(task.getOrderId(), task.getMerchantCode());
		if (payOrderVO == null) {
			return;
		}
		// 2.判断VO是否已关闭, 关闭则直接返回
		if (payOrderVO.isClose()) {
			return;
		}

		// 3.订单未关闭,但已经回调成功
		if (payOrderVO.getStepSuccess(PayStepEnum.STEP5_NOTIFY) != null) {
			PayOrderLogPO logPO = savePayOrderLog(payOrderVO, PayStepEnum.STEP5_NOTIFY, task.getNotifyUrl(), task.getReqContent(), null, null, 0,
					"系统自动关闭订单", YesNoEnum.YES, null);
			payOrderVO.getOrderLogPOs().add(logPO);
			return;
		}

		// 4.支付通道已经回调,还未回调商户方
		PayOrderLogPO resultStep = payOrderVO.getStepLast(PayStepEnum.STEP4_PAY_RESULT);
		if (resultStep == null) {
			// 如果不存在异步结果通知记录，则表示时序出现问题，不符合正常逻辑，不处理
			return;

		}

		if (binder.isSendSuccess() && !binder.isError()) {
			// 如果异步通知业务成功，并且没有异常，则写入日志
			savePayOrderLog(payOrderVO, PayStepEnum.STEP5_NOTIFY, task.getNotifyUrl(), task.getReqContent(), binder.getRspContent(), null,
					binder.getResultCode(), binder.getResultMsg(), YesNoEnum.YES, null);
		}
	}

	/**
	 * 获取支付验证码
	 * 
	 * @author buyi
	 * @date 2017-04-20 20:29:33
	 * @since v1.0.0
	 * @param payOrderVO
	 * @param phone
	 * @return
	 * @throws Throwable
	 */
	public SendVerifyCodeYYContent sendVerifyCode(PayOrderVO payOrderVO, String phone) throws Throwable {
		String paymentCode = payOrderVO.getPayOrderExtPO().getActPaymentCode();

		// 校验验证码CD时长
		checkSenVerifyCodeCoolDown(payOrderVO.getMerchantCode(), payOrderVO.getPayOrderId());

		// 封装发送验证码实体
		ThirdPayService thirdPayService = adapterService.adapterPayService(PaymentEnum.fromValue(paymentCode));
		SendVerifyCodeYYParams sendVerifyCodeYYParams = new SendVerifyCodeYYParams();
		sendVerifyCodeYYParams.setPayOrderId(payOrderVO.getPayOrderId());
		sendVerifyCodeYYParams.setMerchantCode(payOrderVO.getMerchantCode());
		sendVerifyCodeYYParams.setUserId(payOrderVO.getUserId());
		sendVerifyCodeYYParams.setAmount(payOrderVO.getAmount());
		sendVerifyCodeYYParams.setName(payOrderVO.getPayOrderExtPO().getName());
		sendVerifyCodeYYParams.setIdNo(CryptUtil.decrypt(payOrderVO.getPayOrderExtPO().getIdNoCip()));
		sendVerifyCodeYYParams.setCardNo(CryptUtil.decrypt(payOrderVO.getPayOrderExtPO().getCardNoCip()));
		sendVerifyCodeYYParams.setPhone(phone);
		sendVerifyCodeYYParams.setUserIp(payOrderVO.getUserIp());

		// 获取用户支付认证信息，判断是否再次支付标识
		UserPaymentPO userPaymentPO = userPaymentService.query(payOrderVO.getMerchantCode(), payOrderVO.getUserId(), paymentCode);
		boolean isAgain = false;
		if (userPaymentPO != null && YesNoEnum.YES.isValue(userPaymentPO.getState())) {
			if (PaymentEnum.FUIOU.isValue(paymentCode) && StringUtils.isBlank(phone)) {
				// 如果是否富友支付，且手机号传值为空，则从支付鉴权信息中获取
				sendVerifyCodeYYParams.setPhone(userPaymentPO.getPhone());
			}

			isAgain = true;
		}
		sendVerifyCodeYYParams.setAgain(isAgain); // 是否再次支付标识

		SendVerifyCodeYYContent sendVerifyCodeYYContent = thirdPayService.sendVerifyCode(sendVerifyCodeYYParams);
		if (sendVerifyCodeYYContent == null) {
			throw new YYPayException(PayRspCodeEnum.PAY_CODE_GET_PAY_VERIFY_CODE_FAIL, "发送验证码返回null");
		}

		// 插入日志
		// savePayOrderLog(payOrderVO, PayStepEnum.SETP1_VERIFY_CODE,
		// sendVerifyCodeYYContent.getReqUrl(),
		// sendVerifyCodeYYContent.getReqContent(),
		// sendVerifyCodeYYContent.getRspContent(),
		// sendVerifyCodeYYContent.getSerialNo(),
		// sendVerifyCodeYYContent.getResultCode(),
		// sendVerifyCodeYYContent.getResultMsg(), YesNoEnum.NO, null);

		return sendVerifyCodeYYContent;
	}

	/**
	 * 检查获取短信验证码
	 * 
	 * @author buyi
	 * @date 2017-05-05 20:27:01
	 * @since v1.0.0
	 * @param merchantCode
	 * @param payOrderId
	 * @throws YYException
	 *         如果已经获取过，则抛出异常
	 *         {@link PayRspCodeEnum.PAY_CODE_SEND_VARIFY_CODE_COOL_DOWN}
	 */
	public void checkSenVerifyCodeCoolDown(String merchantCode, String payOrderId) throws YYException {
		String key = CacheName.SEND_VERIFY_COOL_DOWN.concat("_").concat(merchantCode).concat("_").concat(payOrderId);
		Long cdTime = cacheService.get(key);
		logger.debug("获取短信验证码冷却时间，merchantCode:{},userId:{},cdTime:{}", merchantCode, payOrderId, cdTime);
		if (cdTime != null && cdTime > 0) {
			throw new YYPayException(PayRspCodeEnum.PAY_CODE_SEND_VARIFY_CODE_COOL_DOWN, "3分钟以内只能获取一次短信验证码,payOderId:" + payOrderId,
					"3分钟以内只能获取一次短信验证码");
		}

		// 新增CD值，如果返回false表示存在并发，提示获取验证码正在冷却中
		boolean result = cacheService.add(key, System.currentTimeMillis(), payConstants.getSendVerifyCoolDownTime());
		if (!result) {
			logger.debug("userId:{},merchantCode:{}获取短信验证码存在重复，提示CD", payOrderId, merchantCode);
			throw new YYPayException(PayRspCodeEnum.PAY_CODE_SEND_VARIFY_CODE_COOL_DOWN, "3分钟以内只能获取一次短信验证码,payOderId:" + payOrderId,
					"3分钟以内只能获取一次短信验证码");
		}
	}

	/**
	 * 支付<br>
	 * 写入支付日志； 如果订单是挂起，或者请求超时，或者成功，生成主动查询任务
	 * 
	 * @author buyi
	 * @date 2017-04-20 20:42:50
	 * @since v1.0.0
	 * @param payOrderVO
	 * @param phone
	 * @param token
	 * @param validCode
	 * @param serialNo
	 * @return
	 * @throws Throwable
	 */
	public PayOrderYYContent payOrder(PayOrderVO payOrderVO, String phone, String token, String validCode, String serialNo) throws Throwable {
		String paymentCode = payOrderVO.getPayOrderExtPO().getActPaymentCode();

		PayOrderYYParams orderYYParams = new PayOrderYYParams();
		orderYYParams.setPayOrderId(payOrderVO.getPayOrderId());
		orderYYParams.setMerchantCode(payOrderVO.getMerchantCode());
		orderYYParams.setUserId(payOrderVO.getUserId());
		orderYYParams.setAmount(payOrderVO.getAmount());
		orderYYParams.setName(payOrderVO.getPayOrderExtPO().getName());
		orderYYParams.setIdNo(CryptUtil.decrypt(payOrderVO.getPayOrderExtPO().getIdNoCip()));
		orderYYParams.setCardNo(CryptUtil.decrypt(payOrderVO.getPayOrderExtPO().getCardNoCip()));
		orderYYParams.setPhone(phone);
		orderYYParams.setToken(token);
		orderYYParams.setValidCode(validCode);
		orderYYParams.setSerialNo(serialNo);
		orderYYParams.setUserIp(payOrderVO.getUserIp());

		// 获取用户支付认证信息，判断是否再次支付标识
		UserPaymentPO userPaymentPO = userPaymentService.query(payOrderVO.getMerchantCode(), payOrderVO.getUserId(), paymentCode);
		boolean isAgain = false;
		if (userPaymentPO != null && YesNoEnum.YES.isValue(userPaymentPO.getState())) {
			// 优化富友支付，如果已经独立鉴权，并且已经存在手机号，则从支付鉴权信息中获取手机号
			if (PaymentEnum.FUIOU.isValue(paymentCode) && StringUtils.isBlank(phone)) {
				// 如果是否富友支付，且手机号传值为空，则从支付鉴权信息中获取
				orderYYParams.setPhone(userPaymentPO.getPhone());
			}

			isAgain = true;
		}
		orderYYParams.setAgain(isAgain); // 是否再次支付标识

		ThirdPayService thirdPayService = adapterService.adapterPayService(PaymentEnum.fromValue(paymentCode));
		PayOrderYYContent content = thirdPayService.payOrder(orderYYParams);

		if (content == null) {
			throw new YYException(PayRspCodeEnum.PAY_CODE_PAY_FAIL, "支付返回信息为空", "支付返回信息为空");
		}

		// 插入日志
		savePayOrderLog(payOrderVO, PayStepEnum.STEP2_SUBMIT_ORDER, content.getReqUrl(), content.getReqContent(), content.getRspContent(),
				content.getSerialNo(), content.getResultCode(), content.getResultMsg(), YesNoEnum.NO, null);

		if (PayRspCodeEnum.PAY_CODE_REQ_TIME_OUT.isValue(content.getResultCode()) || PayRspCodeEnum.PAY_CODE_DEALING.isValue(content.getResultCode())
				|| PayRspCodeEnum.SUCCESS.isValue(content.getResultCode())) {// 如果订单交易超时，或者订单状态为处理中，则生成主动查询任务

			// 如果是支付成功，则将手机号放到缓存中，解决异步回调的时候，同步还没有更新手机号，导致的保存用户鉴权缺少手机号,add by
			// buyi since 1.2.0 20170612
			cacheService.set(payOrderVO.getPayOrderId(), phone, PayConstants.PHONE_CACHE_EXPIRE_SECOND);

			// 更新订单手机号
			payOrderExtService.modifyPhone(payOrderVO.getPayOrderExtPO().getId(), phone);

			try {
				String startTime = DateUtil.datetime2Str(DateUtil.addMinute(new Date(), payConstants.getPayQueryMinuteDelay()));
				payCommonService.saveQueryTask(payOrderVO.getMerchantCode(), payOrderVO.getOrderId(), content.getSerialNo(), QueryTaskTypeEnum.PAY,
						payOrderVO.getPayOrderId(), paymentCode, startTime, null, payConstants.getQueryTaskMax(), "");

			} catch (Exception e) {
				logger.error("插入主动查询任务失败,payOrderId" + payOrderVO.getPayOrderId(), e);
			}

		}

		return content;
	}

	/**
	 * 处理支付异步请求
	 * 
	 * @author buyi
	 * @date 2017-04-20 21:13:06
	 * @since v1.0.0
	 * @param paymentCode
	 * @param notifyContent
	 * @return
	 * @throws Throwable
	 */
	public String processResultNotify(String paymentCode, OrderNotifyContent notifyContent) throws Throwable {
		// 解析业务主键信息
		PayBusiIdVO payOrderIdVO = CommonIdUtils.getPayBusiIdVo(notifyContent.getPayOrderId());

		PayOrderVO payOrderVO = payOrderService.queryPayOrderVO(notifyContent.getPayOrderId(), payOrderIdVO.getMerchantCode());
		if (payOrderVO == null) {
			logger.error("数据库订单缺失，payOrderId:{},merchantCode:{}", notifyContent.getPayOrderId(), payOrderIdVO.getMerchantCode());
			return notifyContent.getRspContent();
		}

		// TODO 如果金额不一致，发出预警
		if (!payOrderVO.getAmount().equals(notifyContent.getAmount())) {
			logger.error("订单：{}，支付金额：{}和异步回调金额：{}不一致", payOrderVO.getPayOrderId(), payOrderVO.getAmount(), notifyContent.getAmount());
		}

		if (payOrderVO.isClose()) { // 如果订单已经结束，则不做任何操作
			logger.info("订单：{}已经关闭，不处理", notifyContent.getPayOrderId());
			return notifyContent.getRspContent();
		}

		if (PayRspCodeEnum.PAY_CODE_VALID_CODE_EXPIRE.isValue(notifyContent.getResultCode())) { // 如果是验证码失效，则不处理
			logger.info("订单：{}验证码失效，不处理", notifyContent.getPayOrderId());
			return notifyContent.getRspContent();
		}

		// 订单有结果响应
		if (payOrderVO.getStepSuccess(PayStepEnum.STEP4_PAY_RESULT) != null) {
			logger.info("订单：{}结果响应为成功，不处理", notifyContent.getPayOrderId());
			return notifyContent.getRspContent();
		}

		// 订单如果没有发起支付请求
		if (payOrderVO.getStepLast(PayStepEnum.STEP2_SUBMIT_ORDER) == null) {
			logger.warn("订单：{}没有收到同步请求结果，支付通道就发起异步通知", notifyContent.getPayOrderId());
		}

		YesNoEnum isClose = YesNoEnum.NO;
		if (PayRspCodeEnum.SUCCESS.isValue(notifyContent.getResultCode())) { // 异步回调成功，且支付状态为成功,

			// 判断手机号是否为空
			String phone = payOrderVO.getPayOrderExtPO().getPhone();
			if (StringUtils.isBlank(phone)) {
				phone = cacheService.get(payOrderVO.getPayOrderId());
			}

			// 验证实名信息，更新用户实名信息
			authService.saveUserAuthAndUserPayment(payOrderVO.getMerchantCode(), payOrderVO.getUserId(),
					CryptUtil.decrypt(payOrderVO.getPayOrderExtPO().getIdNoCip()), payOrderVO.getPayOrderExtPO().getName(),
					payOrderVO.getPayOrderExtPO().getActPaymentCode(), CryptUtil.decrypt(payOrderVO.getPayOrderExtPO().getCardNoCip()),
					payOrderVO.getPayOrderExtPO().getPhone());
		}

		// 写入日志
		savePayOrderLog(payOrderVO, PayStepEnum.STEP4_PAY_RESULT, payOrderVO.getPayOrderExtPO().getPayNotifyUrl(), notifyContent.getReqContent(),
				notifyContent.getRspContent(), notifyContent.getSerialNo(), notifyContent.getResultCode(), notifyContent.getResultMsg(), isClose,
				null);

		// 插入异步通知任务
		saveNotifyTask(payOrderVO, notifyContent.getResultCode(), notifyContent.getResultMsg(), notifyContent.getSerialNo());

		return notifyContent.getRspContent();
	}

	/**
	 * 保存异步通知任务
	 * 
	 * @author buyi
	 * @date 2017-04-25 16:25:27
	 * @since v1.0.0
	 * @param payOrderVO
	 * @param resultCode
	 * @param resultMsg
	 * @param serialNo
	 */
	private void saveNotifyTask(PayOrderVO payOrderVO, int resultCode, String resultMsg, String serialNo) {
		try {
			// 获取商户信息,得到签名密钥
			MerchantConfigPO merchantConfigPO = merchantConfigService.queryByMerchantCode(payOrderVO.getMerchantCode());
			if (merchantConfigPO == null) {
				throw new YYException(PayRspCodeEnum.PAY_CODE_MERCHANT_NOT_EXISTS, "商户：" + payOrderVO.getMerchantCode() + "信息不存在");
			}

			ResultNotifyParams resultNotifyParams = new ResultNotifyParams();
			NotifyParams notifyParams = new NotifyParams();
			notifyParams.setResultCode(resultCode);
			notifyParams.setResultMsg(resultMsg);
			notifyParams.setMerchantCode(payOrderVO.getMerchantCode());
			notifyParams.setOrderId(payOrderVO.getOrderId());
			notifyParams.setUserId(payOrderVO.getUserId());
			notifyParams.setPaymentCode(payOrderVO.getPayOrderExtPO().getActPaymentCode());
			notifyParams.setSerialNo(serialNo);
			notifyParams.setAmount(payOrderVO.getAmount());
			notifyParams.setName(payOrderVO.getPayOrderExtPO().getName());
			notifyParams.setIdNo(CryptUtil.decrypt(payOrderVO.getPayOrderExtPO().getIdNoCip()));
			notifyParams.setCardNo(CryptUtil.decrypt(payOrderVO.getPayOrderExtPO().getCardNoCip()));
			notifyParams.setBankCode(payOrderVO.getPayOrderExtPO().getBankCode());
			notifyParams.setBankName(payOrderVO.getPayOrderExtPO().getBankName());
			notifyParams.setPhone(payOrderVO.getPayOrderExtPO().getPhone());

			resultNotifyParams.setSign(notifyParams.sign(merchantConfigPO.getSignKey()));
			resultNotifyParams.setParams(notifyParams);

			// 保存异步通知任务
			String startTime = DateUtil.datetime2Str(DateUtil.addSecond(new Date(), payConstants.getNotifyStartTimeDelay()));
			payCommonService.saveNotifyTask(payOrderVO.getMerchantCode(), payOrderVO.getOrderId(), serialNo, NotifyTaskTypeEnum.PAY,
					payOrderVO.getPayOrderId(), payOrderVO.getNotifyUrl(), JacksonUtil.noemptyMapper.toJson(resultNotifyParams), startTime, null,
					payConstants.getMaxNo(), null);

		} catch (Throwable e) {
			logger.error("订单：" + payOrderVO.getPayOrderId() + "保存异步通知任务，出现未知异常", e);
		}
	}

	public CounterContent getCounteInfo(PayOrderVO payOrderVO) throws Exception {

		CounterContent content = new CounterContent();
		content.setPayOrderId(payOrderVO.getPayOrderId());
		content.setUserIp(payOrderVO.getUserIp());
		content.setMerchantCode(payOrderVO.getMerchantCode());
		content.setPaymentCode(payOrderVO.getPayOrderExtPO().getActPaymentCode());

		content.setAmount(payOrderVO.getAmount());
		content.setBankName(payOrderVO.getPayOrderExtPO().getBankName());
		content.setdIdNo(payOrderVO.getPayOrderExtPO().getIdNo());
		content.setdName(DesensitizationUtil.convertRealName(payOrderVO.getPayOrderExtPO().getName()));
		content.setdCardNo(payOrderVO.getPayOrderExtPO().getCardNo());
		content.setPaymentName(PaymentEnum.fromValue(payOrderVO.getPayOrderExtPO().getActPaymentCode()).getRemark());
		// 默认填充手机号码
		// content.setPhone(payOrderVO.getPayOrderExtPO().getPhone());

		// 查询限额
		content.setDisplayAmount(new java.text.DecimalFormat("#0.00").format(AmountUtil.fenToYuan(payOrderVO.getAmount())));

		//// 兼容富友存管版本，新增主体类型、支付类型,edit by yuch since 2.0.0
		// PaymentQuotaPO paymentQuotaPO = paymentQuotaService.query(payOrderVO.getMerchantCode(), payOrderVO.getPayOrderExtPO().getActPaymentCode(),
		// payOrderVO.getPayOrderExtPO().getBankCode());
		PaymentQuotaPO paymentQuotaPO = paymentQuotaService.query(payOrderVO.getMerchantCode(), payOrderVO.getPayOrderExtPO().getActPaymentCode(),
				payOrderVO.getPayOrderExtPO().getBankCode(), UserTypeEnum.NATURAL_PERSON.getValue(), PayModeEnum.AUTH_PAY.getValue());

		if (paymentQuotaPO != null) {
			content.setSingleQuota(QuotaUtil.covertToCN(paymentQuotaPO.getSingleQuota(), 0));
			content.setDayQuota(QuotaUtil.covertToCN(paymentQuotaPO.getDayQuota(), 0));
			content.setMonthQuota(QuotaUtil.covertToCN(paymentQuotaPO.getMonthQuota(), 0));
		}

		// 查询银行信息
		BankConfigPO bankConfigPO = bankConfigService.queryByBankCode(payOrderVO.getPayOrderExtPO().getBankCode());
		if (bankConfigPO != null) {
			content.setBankEnName(bankConfigPO.getEnName());
		}

		// 取银行后4位
		String cardNo = CryptUtil.decrypt(payOrderVO.getPayOrderExtPO().getCardNoCip());
		content.setdTailCardNo(cardNo.substring(cardNo.length() - 4));

		return content;
	}

	/**
	 * 校验订单支付结果
	 * 
	 * @author buyi
	 * @date 2017-05-09 11:22:13
	 * @since v1.0.0
	 * @param payOrderVO
	 * @return 如果订单不存在、已经异步回调、已经请求支付，则返回结果{@link PayOrderYYContent},如果没有产生支付，则返回null
	 * @throws YYException
	 *         订单已经关闭/失效，则抛出异常
	 */
	public PayOrderYYContent validatePayOrderForPurchase(PayOrderVO payOrderVO) throws YYException {
		if (payOrderVO == null || payOrderVO.getPayOrderExtPO() == null) {
			throw new YYPayException(PayRspCodeEnum.COMMON_ERROR_OBJECT_NOT_FOUND, "订单不存在", "无效订单");
		}

		PayOrderLogPO resultStep = payOrderVO.getStepLast(PayStepEnum.STEP4_PAY_RESULT);
		// 如果订单已经有结果回调，则返回结果
		if (resultStep != null) {
			return new PayOrderYYContent(resultStep.getResultCode(), resultStep.getResultMsg());
		}

		PayOrderLogPO submitOrderStep = payOrderVO.getStepLast(PayStepEnum.STEP2_SUBMIT_ORDER);
		// 如果订单请求过支付，且状态为成功、超时、挂起,则返回结果
		if (submitOrderStep != null && (PayRspCodeEnum.SUCCESS.isValue(submitOrderStep.getResultCode())
				|| PayRspCodeEnum.PAY_CODE_REQ_TIME_OUT.isValue(submitOrderStep.getResultCode())
				|| PayRspCodeEnum.PAY_CODE_DEALING.isValue(submitOrderStep.getResultCode()))) {
			return new PayOrderYYContent(submitOrderStep.getResultCode(), submitOrderStep.getResultMsg());
		}

		if (payOrderVO.isExpire()) { // 如果订单状态是已经关闭，或者过期
			throw new YYPayException(PayRspCodeEnum.PAY_CODE_PAY_ORDER_IS_CLOSE, "订单：" + payOrderVO.getPayOrderId() + "已经失效", "订单超时，请重新购买");
		}

		if (payOrderVO.isClose()) { // 如果订单状态是已经关闭，或者过期
			throw new YYPayException(PayRspCodeEnum.PAY_CODE_PAY_ORDER_IS_CLOSE, "订单：" + payOrderVO.getPayOrderId() + "已经关闭", "订单已关闭");
		}

		return null;
	}

	/**
	 * 查询支付订单结果
	 * 
	 * @param orderId
	 * @param merchantCode
	 * @param initiative
	 * @return
	 * @throws Exception
	 */
	public QueryPayOrderContent queryPayOrderResult(String orderId, String merchantCode, QueryTypeEnum initiative) throws Exception {
		PayOrderVO payOrderVO = queryPayOrder(orderId, merchantCode, initiative);
		return transformQueryPayResult(payOrderVO);
	}

	/**
	 * 转换支付查询结果
	 * 
	 * @param payOrderVO
	 * @return
	 */
	private QueryPayOrderContent transformQueryPayResult(PayOrderVO payOrderVO) {
		QueryPayOrderContent content = new QueryPayOrderContent();
		// 1.没有找到相关订单
		if (payOrderVO == null) {
			content.setResultCode(PayRspCodeEnum.PAY_CODE_ORDER_NOT_FOUND.getValue());
			content.setResultMsg("订单不存在");
			return content;
		}

		content.setMerchantCode(payOrderVO.getMerchantCode());
		content.setOrderId(payOrderVO.getOrderId());
		content.setUserId(payOrderVO.getUserId());
		if (payOrderVO.getPayOrderExtPO() != null) {
			content.setPaymentCode(payOrderVO.getPayOrderExtPO().getActPaymentCode());
		}
		content.setAmount(payOrderVO.getAmount());

		// 2.订单如果没有操作记录,订单没有进行过同步支付请求
		PayOrderLogPO submitStep = payOrderVO.getStepLast(PayStepEnum.STEP2_SUBMIT_ORDER);
		if (payOrderVO.getOrderLogPOs().isEmpty() || submitStep == null) {
			if (payOrderVO.isExpire()) {
				// 2.1 如果订单失效，则返回订单已经失效
				content.setResultCode(PayRspCodeEnum.PAY_CODE_PAY_ORDER_IS_CLOSE.getValue());
				content.setResultMsg("订单已失效");
				return content;
			} else {
				// 2.2如果没有支付记录，则返回订单未支付
				content.setResultCode(PayRspCodeEnum.PAY_CODE_INFO_UNPAY.getValue());
				content.setResultMsg("订单未支付");
				return content;
			}
		}

		// 3.如果异步回调，则返回异步回调结果
		PayOrderLogPO resultStep = payOrderVO.getStepLast(PayStepEnum.STEP4_PAY_RESULT);
		if (resultStep != null) {
			content.setResultCode(resultStep.getResultCode());
			content.setResultMsg(resultStep.getResultMsg());
			return content;
		}

		// 4.如果有查询结构，则返回查询结果
		PayOrderLogPO queryStep = payOrderVO.getStepLast(PayStepEnum.STEP3_QUERY_RESULT);
		if (queryStep != null) {
			content.setResultCode(queryStep.getResultCode());
			content.setResultMsg(queryStep.getResultMsg());
			return content;
		}

		// 5.如果未知
		content.setResultCode(PayRspCodeEnum.PAY_CODE_PAY_FAIL.getValue());
		content.setResultMsg("支付失败，系统未知异常");

		return content;
	}
}
